﻿using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Build.Utilities;
using Microsoft.Build.Framework;
using System.Globalization;
using Microsoft.Win32;
using System.ServiceProcess;
using Microsoft.SharePoint.Administration;
using System.IO;
using System.Collections;
using Microsoft.SharePoint.Utilities;
using System.Reflection;
using System.Net;

namespace SPALM
{
  /// <summary>
  /// Checks conditions e.g. to be used before a deployment
  /// </summary>
  public class PreConditions : CoreTask
  {
    public PreConditions()
      : base()
    {
    }
    
    private const string DisplayMSBuildExtensionVersion = "DisplayMSBuildExtensionVersion";
    private const string CheckMOSSInstalled = "CheckMOSSInstalled";
    private const string CheckWSSInstalled = "CheckWSSInstalled";
    private const string CheckMinimalVersionInstalled = "CheckMinimalVersionInstalled";
    private const string CheckUserIsAdministrator = "CheckUserIsAdministrator";
    private const string CheckAdminServiceRunning = "CheckAdminServiceRunning";
    private const string CheckTimerServiceRunning = "CheckTimerServiceRunning";
    private const string CheckSolutionDeployed = "CheckSolutionDeployed";
    private const string CheckWebApplicationExists = "CheckWebApplicationExists";
    private const string CheckSiteExists = "CheckSiteExists";
    private const string CheckWebExists = "CheckWebExists";
    private const string CheckPrerequisiteSolutions = "CheckPrerequisiteSolutions";
    private const string CheckIfSolutionExists = "CheckIfSolutionExists";
    private const string GetWebApplicationSolutions = "GetWebApplicationSolutions";
    private const string CheckSolutionDeployment = "CheckSolutionDeployment";
    private const string CheckSolutionUndeployment = "CheckSolutionUndeployment";
    private const string CheckSolutionRemove = "CheckSolutionRemove";
    private const string WaitForSolutionDeploymentFinished = "WaitForSolutionDeploymentFinished";
    private const string GetSTSADMPath = "GetSTSADMPath";
    private const string GetPowerShellConsolePath = "GetPowerShellConsolePath";
    private const string Sleep = "Sleep";
    private const string Ping = "Ping";

    public virtual string TaskAction { get; set; }

    public virtual string MinimalVersion { get; set; }

    public virtual string Url { get; set; }

    public virtual string Solutions { get; set; }


    public virtual int MilliSeconds { get; set; }

    [Output]
    public ITaskItem[] WebApplicationSolutions
    {
      get
      {
        return webapplicationSolutions;
      }
    }

    [Output]
    public string STSADMPath
    {
      get
      {
        string path = Path.Combine(SPUtility.GetGenericSetupPath("BIN"), "stsadm.exe");
        Log.LogMessageFromText("Retrieving path to stsadm: " + path, MessageImportance.Low);
        return path;
      }
    }

    [Output]
    public string PowerShellConsolePath
    {
      get
      {
        string path = Path.Combine(SPUtility.GetGenericSetupPath("Config"), "POWERSHELL\\Registration\\psconsole.psc1");
        Log.LogMessageFromText("Retrieving path to powershell console: " + path, MessageImportance.Low);
        return path;
      }
    }

    [Output]
    public ITaskItem[] GloballySolutions
    {
      get
      {
        return globallySolutions;
      }
    }

    private ITaskItem[] webapplicationSolutions;
    private ITaskItem[] globallySolutions;

    public override bool Execute()
    {
      switch (this.TaskAction)
      {
        case DisplayMSBuildExtensionVersion:
          return this.RunDisplayMSBuildExtensionVersion();
        case CheckMOSSInstalled:
          return this.RunCheckMOSSInstalled();
        case CheckWSSInstalled:
          return this.RunCheckWSSInstalled();
        case CheckMinimalVersionInstalled:
          return this.RunCheckMinimalVersionInstalled();
        case CheckUserIsAdministrator:
          return this.RunCheckUserIsAdministrator();
        case CheckAdminServiceRunning:
          return this.RunCheckAdminServiceRunning();
        case CheckTimerServiceRunning:
          return this.RunCheckTimerServiceRunning();
        case CheckWebApplicationExists:
          return this.RunCheckWebApplicationExists();
        case CheckSiteExists:
          return this.RunCheckSiteExists();
        case CheckWebExists:
          return this.RunCheckWebExists();
        case CheckPrerequisiteSolutions:
          return this.RunCheckPrerequisiteSolutions();
        case CheckIfSolutionExists:
          return this.RunCheckIfSolutionExists();
        case GetWebApplicationSolutions:
          return this.RunGetWebApplicationSolutions();
        case CheckSolutionDeployment:
          return this.RunCheckSolutionDeployment();
        case CheckSolutionUndeployment:
          return this.RunCheckSolutionUndeployment();
        case CheckSolutionRemove:
          return this.RunCheckSolutionRemove();
        case WaitForSolutionDeploymentFinished:
          return this.RunWaitForSolutionDeploymentFinished();
        case GetSTSADMPath:
          return this.RunGetSTSADMPath();
        case GetPowerShellConsolePath:
          return this.RunGetPowerShellConsolePath();
        case Sleep:
          return this.RunSleep();
        case Ping:
          return this.RunPing();
        default:
          Log.LogError(string.Format(CultureInfo.CurrentCulture, "Invalid TaskAction passed: {0}", this.TaskAction));
          return false;
      }
    }

    private bool RunDisplayMSBuildExtensionVersion()
    {
      Log.LogMessageFromText("Version of SPALM.dll: " + System.Diagnostics.FileVersionInfo.GetVersionInfo(Assembly.GetExecutingAssembly().Location).FileVersion + "", MessageImportance.Normal);
      return true;
    }

    private bool RunSleep()
    {
      Log.LogMessageFromText("Sleeping " + MilliSeconds + " milliseconds...", MessageImportance.Normal);
      System.Threading.Thread.Sleep(this.MilliSeconds);
      Log.LogMessageFromText("", MessageImportance.Normal);
      return true;
    }

    private bool RunPing()
    {
      Log.LogMessageFromText("Pinging server " + Url + "...", MessageImportance.Normal);

      bool serverOK = false;
      int maxWaiting = 20;
      int currentWaiting = 0;

      try
      {
        while (!serverOK)
        {
          serverOK = true;

          try
          {
            HttpWebRequest  webRequest = (HttpWebRequest)WebRequest.Create(Url);
            webRequest.Timeout = 3000;
            webRequest.Credentials = CredentialCache.DefaultCredentials;
            HttpWebResponse response = (HttpWebResponse)webRequest.GetResponse();
            Log.LogMessage("Server returned status " + response.StatusCode + " (" + response.StatusDescription + ")", MessageImportance.Low);
            response.Close();
            break;
          }
          catch (WebException ex)
          {
            if (ex.Status == WebExceptionStatus.Timeout)
            {
              //Log.LogMessage("Exception '" + ex.Message, MessageImportance.High);
              Log.LogMessage("Request timed out, trying again...", MessageImportance.Low);
              serverOK = false;
            }
            else
            {
              //server answered by url was not found
              Log.LogWarning("Server answered, but returned with error." + ex.Status);
              break;              
            }
          }

          if (currentWaiting > maxWaiting)
          {
            
            break;
          }

          if (!serverOK)
          {
            System.Threading.Thread.Sleep(1000);
            currentWaiting++;
          }
        }
      }
      catch (Exception ex)
      {
        Log.LogErrorFromException(ex);
        return false;
      }

      Log.LogMessageFromText("", MessageImportance.Normal);
      return true;
    }

    private bool RunGetSTSADMPath()
    {
      return true;
    }

    private bool RunGetPowerShellConsolePath()
    {
      return true;
    }

    private bool RunWaitForSolutionDeploymentFinished()
    {

      //waits that no solution deployment is running
      bool deployFinished = false;
      int maxWaiting = 20;
      int currentWaiting = 0;

      try
      {
        while (!deployFinished)
        {
          deployFinished = true;
          foreach (SPSolution solution in SPFarm.Local.Solutions)
          {
            if ((solution.JobExists)) // || (solution.Status == SPObjectStatus.Provisioning) || (solution.Status == SPObjectStatus.Unprovisioning) || (solution.Status == SPObjectStatus.Upgrading))
            {
              Log.LogMessageFromText("Waiting for solution " + solution.Name + "(" + solution.JobStatus.ToString() + ")", MessageImportance.Normal);
              deployFinished = false;
            }
          }

          if (currentWaiting > maxWaiting)
          {
            throw new Exception("Solution deployment failed. Timeout reached.");
            break;
          }

          if (!deployFinished)
          {
            System.Threading.Thread.Sleep(5000);
            currentWaiting++;
          }
        }
      }
      catch (Exception ex)
      {
        Log.LogErrorFromException(ex);
        return false;
      }


      //ExecAdmSvcJobs();

      Log.LogMessageFromText("Solutions successfully deployed.", MessageImportance.Normal);
      return true;
    }

    private bool RunCheckSolutionUndeployment()
    {
      return InternalCheckSolutionDeploymentStatus(false);
    }

    private bool RunCheckSolutionDeployment()
    {
      return InternalCheckSolutionDeploymentStatus(true);
    }

    private bool RunCheckSolutionRemove()
    {
      bool throwException = false;
      string message = "";
      char[] sep = new char[';'];
      string[] solutionNames = Solutions.Split(';');
      try
      {
        SPSolutionCollection solutions = SPFarm.Local.Solutions;

        foreach (string solutionName in solutionNames)
        {
          string checkName = solutionName;
          if (checkName.Contains("\\"))
          {
            checkName = checkName.Substring(checkName.IndexOf("\\") + 1);
          }

          //Log.LogMessageFromText("Checking solution '" + checkName + "'...", MessageImportance.Normal);

          foreach (SPSolution solution in solutions)
          {
            if (solution.Name.Equals(checkName, StringComparison.InvariantCultureIgnoreCase))
            {
              //ups, solution already in solution store
              throwException = true;
              message += "Solution '" + solution.Name + "' still found in solution store; ";
            }
          }
        }
      }
      catch { }

      if (throwException)
      {
        Log.LogErrorFromException(new Exception(message));
        return false;
      }
      return true;
    }

    private bool InternalCheckSolutionDeploymentStatus(bool checkForDeployment)
    {
      bool throwException = false;
      string message = "";

      if (Solutions == null)
      {
        return true;
      }

      if (checkForDeployment)
      {
        //Solutions can come as Customizations\ApplicationName22.Resources1.wsp
        Log.LogMessageFromText("Checking solutions '" + Solutions + "' for successful deployment...", MessageImportance.Normal);
      }
      else
      {
        //Solutions can come as Customizations\ApplicationName22.Resources1.wsp
        Log.LogMessageFromText("Checking solutions '" + Solutions + "' for successful undeployment...", MessageImportance.Normal);
      }

      char[] sep = new char[';'];
      string[] solutionNames = Solutions.Split(';');
      try
      {
        SPSolutionCollection solutions = SPFarm.Local.Solutions;

        foreach (string solutionName in solutionNames)
        {
          string checkName = solutionName;
          if (checkName.Contains("\\"))
          {
            checkName = checkName.Substring(checkName.IndexOf("\\") + 1);
          }

          //Log.LogMessageFromText("Checking solution '" + checkName + "'...", MessageImportance.Normal);

          foreach (SPSolution solution in solutions)
          {
            if (solution.Name.Equals(checkName, StringComparison.InvariantCultureIgnoreCase))
            {
              if (checkForDeployment)
              {
                if (solution.Deployed)
                {
                  //everything: OK
                  Log.LogMessageFromText("Solution '" + checkName + "' deployed successfully", MessageImportance.Normal);
                }
                else
                {
                  throwException = true;
                  message += "Solution '" + checkName + "' not deployed successfully; ";
                  Log.LogMessageFromText("Error: Solution '" + checkName + "' not deployed successfully (" + solution.LastOperationDetails + ")", MessageImportance.Normal);
                }
              }
              else
              {
                if (!solution.Deployed)
                {
                  //everything: OK
                  Log.LogMessageFromText("Solution '" + checkName + "' undeployed successfully", MessageImportance.Normal);
                }
                else
                {
                  throwException = true;
                  message += "Solution '" + checkName + "' not successfully undeployed; ";
                  Log.LogMessageFromText("Error: Solution '" + checkName + "' not undeployed successfully (" + solution.LastOperationDetails + ")", MessageImportance.Normal);
                }
              }
            }
          }
        }

        Log.LogMessageFromText(" ", MessageImportance.Normal);
      }
      catch (Exception ex)
      {
        Log.LogErrorFromException(ex);
        return false;
      }
      if (throwException)
      {
        Log.LogErrorFromException(new Exception(message));
        return false;
      }
      return true;
    }

    private bool DeploymentHasWarnings(SPSolution solution)
    {
      if (solution.LastOperationResult == SPSolutionOperationResult.DeploymentWarningsOccurred)
      {
        return true;
      }
      if (solution.LastOperationResult == SPSolutionOperationResult.RetractionWarningsOccurred)
      {
        return true;
      }
      return false;
    }

    private bool DeploymentFinished(SPSolution solution)
    {
      if (solution.LastOperationResult == SPSolutionOperationResult.DeploymentSucceeded)
      {
        return true;
      }
      if (solution.LastOperationResult == SPSolutionOperationResult.RetractionSucceeded)
      {
        return true;
      }
      return false;
    }

    private bool DeploymentIsInProgress(SPSolution solution)
    {
      if (solution.LastOperationResult == SPSolutionOperationResult.NoOperationPerformed)
      {
        return true;
      }
      return false;
    }

    private bool DeploymentFailed(SPSolution solution)
    {
      if (solution.LastOperationResult == SPSolutionOperationResult.DeploymentFailedFeatureInstall)
      {
        return true;
      }
      if (solution.LastOperationResult == SPSolutionOperationResult.DeploymentFailedFileCopy)
      {
        return true;
      }
      if (solution.LastOperationResult == SPSolutionOperationResult.DeploymentSolutionValidationFailed)
      {
        return true;
      }
      if (solution.LastOperationResult == SPSolutionOperationResult.DeploymentFailedCabExtraction)
      {
        return true;
      }

      if (solution.LastOperationResult == SPSolutionOperationResult.RetractionFailedCouldNotRemoveFeature)
      {
        return true;
      }
      if (solution.LastOperationResult == SPSolutionOperationResult.RetractionFailedCouldNotRemoveFile)
      {
        return true;
      }

      return false;
    }

    private bool RunGetWebApplicationSolutions()
    {
      ArrayList webappSolutionArray = new ArrayList();
      ArrayList globalSolutionArray = new ArrayList();

      if (Solutions == null)
      {
        return true;
      }

      //Solutions can come as Customizations\ApplicationName22.Resources1.wsp
      Log.LogMessageFromText("Getting information from SharePoint which solutions '" + Solutions + "' contains web application resource.", MessageImportance.Normal);

      char[] sep = new char[';'];
      string[] solutionNames = Solutions.Split(';');

      try
      {
        SPSolutionCollection solutions = SPFarm.Local.Solutions;

        foreach (string solutionName in solutionNames)
        {
          string checkName = solutionName;
          if (checkName.Contains("\\"))
          {
            checkName = checkName.Substring(checkName.IndexOf("\\") + 1);
          }

          //Log.LogMessageFromText("Checking solution '" + checkName + "' ", MessageImportance.Normal);


          foreach (SPSolution solution in solutions)
          {
            if (solution.Name.Equals(checkName, StringComparison.InvariantCultureIgnoreCase))
            {
              //solution found
              if (solution.ContainsWebApplicationResource)
              {

                Log.LogMessageFromText("Solution '" + checkName + "' contains web application resource", MessageImportance.Normal);
                webappSolutionArray.Add(ToTaskItem(checkName));
              }
              else
              {
                Log.LogMessageFromText("Solution '" + checkName + "' contains no web application resource", MessageImportance.Normal);
                globalSolutionArray.Add(ToTaskItem(checkName));
              }
            }
          }
        }
      }
      catch (Exception ex)
      {
        Log.LogErrorFromException(ex);
        return false;
      }

      webapplicationSolutions = (ITaskItem[])webappSolutionArray.ToArray(typeof(ITaskItem));
      globallySolutions = (ITaskItem[])globalSolutionArray.ToArray(typeof(ITaskItem));

      return true;
    }

    private bool RunCheckIfSolutionExists()
    {
      if (Solutions == null)
      {
        return true;
      }

      bool throwException = false;
      string message = "";

      string[] solutionIds = Solutions.Split(new char[';']);

      Log.LogMessageFromText("Checking if solutions exists '" + Solutions + "'...", MessageImportance.Normal);
      try
      {
        SPSolutionCollection solutions = SPFarm.Local.Solutions;

        foreach (string solutionId in solutionIds)
        {
          //is the parameter a solution id or a filename
          Guid solutionGuid;
          try
          {
            solutionGuid = new Guid(solutionId);

            foreach (SPSolution solution in solutions)
            {
              if (solution.Id == solutionGuid)
              {
                //solution exists, failure
                throwException = true;
                message += solution.Name + ";";
              }
            }
          }
          catch (Exception)
          {
            string solutionName = "";

            //ok, is not guid, try the filename
            if (Path.IsPathRooted(solutionId))
            {
              if (File.Exists(solutionId))
              {
                solutionName = Path.GetFileName(solutionId);
              }
            }
            else
            {
              solutionName = solutionId;
            }

            //check for the solution with the name
            foreach (SPSolution solution in solutions)
            {
              if (solution.Name.Equals(solutionName, StringComparison.InvariantCultureIgnoreCase))
              {
                //solution exists, failure
                throwException = true;
                message += solutionName + ";";
              }
            }

          }
        }
      }
      catch (Exception ex)
      {
        Log.LogErrorFromException(ex);
        return false;
      }

      if (throwException)
      {
        Log.LogErrorFromException(new Exception("Solutions already installed: " + message));
        return false;
      }
      else
      {
        Log.LogMessageFromText("Checking existing solutions sucessfully finished.", MessageImportance.Normal);
        return true;
      }
    }

    private bool RunCheckPrerequisiteSolutions()
    {
      if (Solutions == null)
      {
        return true;
      }

      bool throwException = false;
      string missingSolutions = "";

      string[] solutionIds = Solutions.Split(new char[';']);

      Log.LogMessageFromText("Checking prerequisite solutions '" + Solutions + "'...", MessageImportance.Normal);
      try
      {
        foreach (string solutionId in solutionIds)
        {
          if (solutionId.Trim() != "")
          {
            Log.LogMessageFromText("Checking solution '" + solutionId + "'.", MessageImportance.Normal);
            bool solutionFound = false;
            try
            {
              //try the check with the id
              Guid solutionGuid = new Guid(solutionId);
              SPSolutionCollection solutions = SPFarm.Local.Solutions;
              foreach (SPSolution solution in solutions)
              {
                if (solution.Id == solutionGuid)
                {
                  solutionFound = true;
                }
              }
            }
            catch { }

            if (!solutionFound)
            {
              try
              {
                //try second check with name
                SPSolutionCollection solutions = SPFarm.Local.Solutions;
                foreach (SPSolution solution in solutions)
                {
                  if (solution.Name.Equals(solutionId, StringComparison.InvariantCultureIgnoreCase))
                  {
                    solutionFound = true;
                  }
                }
              }
              catch { }
            }

            if (!solutionFound)
            {
              throwException = true;
              missingSolutions += solutionId + ";";
            }
          }
        }
      }
      catch (Exception ex)
      {
        Log.LogErrorFromException(ex);
        return false;
      }

      if (throwException)
      {
        Log.LogErrorFromException(new Exception("Solutions not found: " + missingSolutions));
        return false;
      }
      else
      {
        Log.LogMessageFromText("Checking prerequisite solutions sucessfully finished.", MessageImportance.Normal);
        return true;
      }
    }

    private bool RunCheckWebExists()
    {
      Log.LogMessageFromText("Checking web '" + Url + "'...", MessageImportance.Normal);
      return true;
    }

    private bool RunCheckSiteExists()
    {
      Log.LogMessageFromText("Checking site collection '" + Url + "'...", MessageImportance.Normal);
      return true;
    }

    private bool RunCheckWebApplicationExists()
    {
      Log.LogMessageFromText("Checking web application '" + Url + "'...", MessageImportance.Normal);
      return true;
    }

    private bool RunCheckTimerServiceRunning()
    {
      Log.LogMessageFromText("Checking SharePoint timer service running (SPTimer) ...", MessageImportance.Normal);
      try
      {
        if (EnsureRunning("SPTimerV3"))
        {
          return true;
        }
      }
      catch { }
      try
      {
        if (EnsureRunning("WSSTimerV3"))
        {
          return true;
        }
      }
      catch { }
      try
      {
        if (EnsureRunning("SPTimerV4"))
        {
          return true;
        }
      }
      catch { }
      try
      {
        if (EnsureRunning("WSSTimerV4"))
        {
          return true;
        }
      }
      catch { }

      Log.LogError("Service SPTimer not running.");
      return false;
    }

    private bool RunCheckAdminServiceRunning()
    {
      Log.LogMessageFromText("Checking admin service running (SPAdmin)...", MessageImportance.Normal);

      try
      {
        if (EnsureRunning("SPAdmin"))
        {
          return true;
        }
      }
      catch { }

      try
      {
        if (EnsureRunning("SPAdminV4"))
        {
          return true;
        }
      }
      catch { }

      Log.LogWarning("Service SPAdmin not running.");
      return true;
    }

    private bool RunCheckUserIsAdministrator()
    {
      Log.LogMessageFromText("Checking user '" + System.Security.Principal.WindowsIdentity.GetCurrent().Name + "' is administrator...", MessageImportance.Normal);
      try
      {
        if (SPFarm.Local != null)
        {
          if (SPFarm.Local.CurrentUserIsAdministrator())
          {
            return true;
          }
        }
        else
        {
          throw new Exception("Farm is not running");
        }
      }
      catch (Exception ex)
      {
        Log.LogErrorFromException(ex);
        return false;
      }
      Log.LogError("User is not administrator.");
      return false;
    }

    private bool RunCheckMinimalVersionInstalled()
    {
      Log.LogMessageFromText("Checking minimal SharePoint version '" + MinimalVersion + "'...", MessageImportance.Normal);
      try
      {
        Version installedVersion = SPFarm.Local.BuildVersion;
        Log.LogMessageFromText("Current SharePoint version is '" + installedVersion + "'", MessageImportance.Normal);
        Version minimalVersion = new Version(MinimalVersion);
        if (installedVersion < minimalVersion)
        {
          Log.LogErrorFromException(new Exception("Installed version " + installedVersion.ToString() + " is lower than necessary minimal version " + minimalVersion));
          return false;
        }
      }
      catch (Exception ex)
      {
        Log.LogWarning("Could not check SharePoint version");
      }
      return true;
    }

    private bool RunCheckWSSInstalled()
    {
      Log.LogMessageFromText("Checking for WSS installation...", MessageImportance.Normal);

      string name = @"SOFTWARE\Microsoft\Shared Tools\Web Server Extensions\12.0";
      string name14 = @"SOFTWARE\Microsoft\Shared Tools\Web Server Extensions\14.0";
      try
      {
        RegistryKey key = Registry.LocalMachine.OpenSubKey(name);
        if (key != null)
        {
          object val = key.GetValue("SharePoint");
          if (val != null && val.Equals("Installed"))
          {
            return true;
          }
        }

      }
      catch (Exception ex)
      {

      }
      try
      {
        RegistryKey key = Registry.LocalMachine.OpenSubKey(name14);
        if (key != null)
        {
          object val = key.GetValue("SharePoint");
          if (val != null && val.Equals("Installed"))
          {
            return true;
          }
        }

      }
      catch (Exception ex)
      {

      }
      Log.LogError("WSS not installed.");
      return false;
    }

    private bool RunCheckMOSSInstalled()
    {
      Log.LogMessageFromText("Checking for MOSS installation...", MessageImportance.Normal);

      string name = @"SOFTWARE\Microsoft\Office Server\12.0";
      string name14 = @"SOFTWARE\Microsoft\Office Server\14.0";
      try
      {
        RegistryKey key = Registry.LocalMachine.OpenSubKey(name);
        if (key != null)
        {
          string versionStr = key.GetValue("BuildVersion") as string;
          if (versionStr != null)
          {
            Version buildVersion = new Version(versionStr);
            if (buildVersion.Major == 12)
            {
              return true;
            }
          }
        }
      }
      catch (Exception ex)
      {

      }

      try
      {
        RegistryKey key = Registry.LocalMachine.OpenSubKey(name14);
        if (key != null)
        {
          string versionStr = key.GetValue("BuildVersion") as string;
          if (versionStr != null)
          {
            Version buildVersion = new Version(versionStr);
            if (buildVersion.Major == 14)
            {
              return true;
            }
          }
        }
      }
      catch (Exception ex)
      {

      }
      Log.LogError("MOSS not installed.");
      return false;
    }

    private void ExecAdmSvcJobs()
    {

      // First run the OOTB execadmsvcjobs on the local machine to make sure that any local jobs get executed
      int timeout = 10;
      int runningCount = 0;
      foreach (SPServer server in SPFarm.Local.Servers)
      {
        // Only look at servers with a valid role.
        if (server.Role == SPServerRole.Invalid)
          continue;

        // Don't need to check locally as we just ran the OOTB command locally so skip the local server.
        if (server.Id.Equals(SPServer.Local.Id))
          continue;

        bool stillExecuting;
        //Log.LogMessageFromText("\rChecking jobs on {0}", server.Name);

        do
        {
          stillExecuting = CheckApplicableRunningJobs(server, false);
          runningCount++;

          // If jobs are still executing then sleep for 1 second.
          if (stillExecuting)
          {
            System.Threading.Thread.Sleep(1000);
          }
          if (runningCount > timeout)
          {
            stillExecuting = false;
            return;
          }
        }
        while (stillExecuting);
      }
    }

    /// <summary>
    /// Checks for applicable running jobs.
    /// </summary>
    /// <param name="server">The server.</param>
    /// <param name="quiet">if set to <c>true</c> [quiet].</param>
    /// <returns></returns>
    private bool CheckApplicableRunningJobs(SPServer server, bool quiet)
    {
      foreach (KeyValuePair<Guid, SPService> current in GetProvisionedServices(server))
      {
        SPService service = current.Value;
        SPAdministrationServiceJobDefinitionCollection definitions = new SPAdministrationServiceJobDefinitionCollection(service);

        if (CheckApplicableRunningJobs(server, definitions, quiet))

          return true; // We've found running jobs so no point looking any further.  

        SPWebService service2 = service as SPWebService;
        if (service2 != null)
        {
          foreach (SPWebApplication webApplication in service2.WebApplications)
          {
            definitions = new SPAdministrationServiceJobDefinitionCollection(webApplication);

            if (CheckApplicableRunningJobs(server, definitions, quiet))
              return true;
          }
        }
      }
      return false;
    }

    /// <summary>
    /// Checks for applicable running jobs.
    /// </summary>
    /// <param name="server">The server.</param>
    /// <param name="jds">The job definitions to consider.</param>
    /// <param name="quiet">if set to <c>true</c> [quiet].</param>
    /// <returns></returns>
    private bool CheckApplicableRunningJobs(SPServer server, SPAdministrationServiceJobDefinitionCollection jds, bool quiet)
    {
      bool stillExecuting = false;


      foreach (SPJobDefinition definition in jds)
      {
        if (string.IsNullOrEmpty(definition.Name))
          continue;

        bool isApplicable = false;
        if (!definition.IsDisabled)
          isApplicable = ((definition.Server == null) || definition.Server.Id.Equals(server.Id));

        if (!isApplicable)
        {
          // If it's not applicable then we don't really care if it's running or not.
          continue;
        }


        Log.LogMessageFromText("Waiting on " + definition.Name, MessageImportance.Normal);

        stillExecuting = true;
      }
      return stillExecuting;
    }

    /// <summary>
    /// Gets the provisioned services.
    /// </summary>
    /// <param name="server">The server.</param>
    /// <returns></returns>
    private Dictionary<Guid, SPService> GetProvisionedServices(SPServer server)
    {
      Dictionary<Guid, SPService> dictionary = new Dictionary<Guid, SPService>(8);

      foreach (SPServiceInstance serviceInstance in server.ServiceInstances)
      {
        SPService service = serviceInstance.Service;
        if (serviceInstance.Status == SPObjectStatus.Online)
        {
          if (dictionary.ContainsKey(service.Id))
            continue;
          dictionary.Add(service.Id, service);
        }
      }
      return dictionary;

    }

    /// <summary>
    /// This class mimics the internal equivalent and is used because the base class is abstract.
    /// </summary>
    internal class SPAdministrationServiceJobDefinitionCollection : SPPersistedChildCollection<SPAdministrationServiceJobDefinition>
    {
      /// <summary>
      /// Initializes a new instance of the <see cref="SPAdministrationServiceJobDefinitionCollection"/> class.
      /// </summary>
      /// <param name="service">The service.</param>
      internal SPAdministrationServiceJobDefinitionCollection(SPService service)
        : base(service)
      {
      }

      /// <summary>
      /// Initializes a new instance of the <see cref="SPAdministrationServiceJobDefinitionCollection"/> class.
      /// </summary>
      /// <param name="webApplication">The web application.</param>

      internal SPAdministrationServiceJobDefinitionCollection(SPWebApplication webApplication)
        : base(webApplication)
      {
      }
    }
  }
}

